package com.ocom.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.annotation.CachingConfigurationSelector;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;


import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.time.Duration;

@Configuration
public class RedisConfig  extends CachingConfigurerSupport {
    /**定义缓存key的生成器，不定义也可以使用默认的，aop方式
     * @return
     * */
    @Bean
    @Override
    public KeyGenerator keyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder stringBuilder = new StringBuilder();
                //类目
                stringBuilder.append(target.getClass().getName());
                stringBuilder.append("::");
                //方法名
                stringBuilder.append(method.getName());
                //方法没有参数就没有这个循环
                for (Object param:params) {
                    stringBuilder.append(param.toString());
                }
                return stringBuilder.toString();
            }
        };
    }
    /**定制aop方式
     * 初始化一个Cache管理器对象,不定义也可以，有默认的，单假如希望再基于AOP
     * 方式Redis的操作时,按照指定的序列化方式进行序列化
     * @param connectionFactory
     * @return
     * */
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory){
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //缓存失效时长
                .entryTtl(Duration.ofSeconds(60*60))
                //配置key的序列化
                .serializeKeysWith(RedisSerializationContext
                        .SerializationPair.fromSerializer(new StringRedisSerializer()))
                //配置value的序列化
                .serializeValuesWith(RedisSerializationContext
                        .SerializationPair.fromSerializer(
                                new Jackson2JsonRedisSerializer<Object>(Object.class)))
                //不缓存null值
                .disableCachingNullValues();
        return RedisCacheManager
                .builder(connectionFactory)
                .cacheDefaults(config) //修改默认配置
                .transactionAware() //启动默认事务
                .build();
    }
    //定制redistemplate
    //代码定制参考RedisAutoConfiguration类
    @Bean
    @ConditionalOnMissingBean(
            name = {"redisTemplate"}
    )
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        //1.构建RedisTemplate对象
        RedisTemplate<Object, Object> template = new RedisTemplate();
        //2.设置连接工厂
        template.setConnectionFactory(redisConnectionFactory);
        //3.定义redistemplate对象的序列化方式
        //3.1. 定义key的序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        //3.2.定义value的序列方式
        template.setValueSerializer(jsonRedisSerializer());
        template.setHashValueSerializer(jsonRedisSerializer());
        //4.修改RedisTemplate 默认特性(除了序列化，其它原有特性不丢)
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public Jackson2JsonRedisSerializer jsonRedisSerializer(){
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //定义序列化方式(在这里选择jackson)
        ObjectMapper objectMapper = new ObjectMapper();
        //设置要序列化的域(属性)
        //any表示任意级别访问修饰符修饰的属性 private,public,protected
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //激活默认类型(存储json时会添加类型信息到json串中)
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        jsonRedisSerializer.setObjectMapper(objectMapper);
        return jsonRedisSerializer;
    }
}
